﻿using SuperX.Common;
using SuperX.Common.Log;
using SuperX.DataService;
using SuperX.DataService.Abstract;
using HslCommunication.Core;
using HslCommunication.ModBus;
using System;
using System.Collections.Generic;
using System.Text;

namespace SuperX.ModbusDriver
{
    public abstract class ModbusBase : DriverBase, IPLCDriver
    {
        protected IReadWriteNet PlcReadWrite = null;
        public virtual IByteTransform ByteTransform { get; }

        public abstract IReadWriteNet ReadWriteNet { get; }

        public override int Port { get; set; } = 560;

        public override int PDU
        {
            // get { return 252; }
            //get { return 256; }
            /* 更新人：yjz
              更新日期：20171125
              更新原因： 在 modbus——TCP中协议规定如下：
                       ADU=MBAP+功能码+数据    其中 ADU 256字节，MBAP 7字节，功能码1字节，数据为248字节
                       PDU=功能码+数据
                       所以PDU应为： 249字节
             */
            get { return 249; } //0xF9 十进制为249
        }

        private IDataServer _parent;

        protected bool _connected = false;

        /// <summary>
        /// 是否关闭
        /// </summary>
        public override bool IsClosed
        {
            get
            {
                return PlcReadWrite == null || !_connected;
            }
        }

        private Byte _salveId;

        /// <summary>
        /// SalveId
        /// </summary>
        public Byte SalveId
        {
            get { return _salveId; }
            set { _salveId = value; }
        }

        public DataFormat DataFormat { get; set; }

        public ModbusBase()
        {
        }

        public ModbusBase(IDataServer server, string name, string nodeName) : base(server, name, nodeName)
        {
        }

        public override IGroup AddGroup(string name, int updateRate, float deadBand = 0, bool active = false)
        {
            ByteTransform.DataFormat = DataFormat;
            ModbusGroup group = new ModbusGroup(name, updateRate, active, this, ByteTransform);
            DriverGroup.Add(group);
            return group;
        }

        public byte[] ReadBytes(DeviceAddress address, ushort size)
        {
            if (!_connected || PlcReadWrite == null) return null;
            var addr = address.Addr;
#if DEBUG
            //Logger.Info($"读取Bytes的范围:{size}");
#endif
            var result = PlcReadWrite.Read(addr, (ushort)(size));

            if (result.IsSuccess) return result.Content;
            else
            {
                OnError1(result.Message);
                if (result.Message.Contains("连接失败")) _connected = false;
                return null;
            }
        }

        public bool[] ReadBools(DeviceAddress address, ushort size)
        {
            if (!_connected || PlcReadWrite == null) return null;
            var addr = address.Addr;
            Logger.Info($"读取Bools的范围:{size}");
            var result = PlcReadWrite.ReadBool(addr, (ushort)(size));

            if (result.IsSuccess) return result.Content;
            else
            {
                OnError1(result.Message);
                if (result.Message.Contains("连接失败")) _connected = false;
                return null;
            }
        }

        /// <summary>
        /// 读取32位整数
        /// </summary>
        /// <param name="address">标签变量地址结构</param>
        /// <returns></returns>
        public ItemData<int> ReadInt32(DeviceAddress address)
        {
            var result = PlcReadWrite.ReadInt32(address.Addr);
            if (!result.IsSuccess)
                return new ItemData<int>(0, 0, QUALITIES.NG);
            else
                return new ItemData<int>(result.Content, 0, QUALITIES.OK);
        }

        public ItemData<uint> ReadUInt32(DeviceAddress address)
        {
            var result = PlcReadWrite.ReadUInt32(address.Addr);
            if (!result.IsSuccess)
                return new ItemData<uint>(0, 0, QUALITIES.NG);
            else
                return new ItemData<uint>(result.Content, 0, QUALITIES.OK);
        }

        public ItemData<ushort> ReadUInt16(DeviceAddress address)
        {
            var result = PlcReadWrite.ReadUInt16(address.Addr);
            if (!result.IsSuccess)
                return new ItemData<ushort>(0, 0, QUALITIES.NG);
            else
                return new ItemData<ushort>(result.Content, 0, QUALITIES.OK);
        }

        /// <summary>
        /// 读取16位整数
        /// </summary>
        /// <param name="address">标签变量地址结构</param>
        /// <returns></returns>
        public ItemData<short> ReadInt16(DeviceAddress address)
        {
            var result = PlcReadWrite.ReadInt16(address.Addr);
            if (!result.IsSuccess)
                return new ItemData<short>(0, 0, QUALITIES.NG);
            else
                return new ItemData<short>(result.Content, 0, QUALITIES.OK);
        }

        /// <summary>
        /// 读取1字节
        /// </summary>
        /// <param name="address">标签变量地址结构</param>
        /// <returns></returns>
        public ItemData<byte> ReadByte(DeviceAddress address)
        {
            var result = PlcReadWrite.ReadInt16(address.Addr);
            if (!result.IsSuccess)
                return new ItemData<byte>(0, 0, QUALITIES.NG);
            else
                return new ItemData<byte>((byte)result.Content, 0, QUALITIES.OK);
        }

        /// <summary>
        /// 读取字符串
        /// </summary>
        /// <param name="address">标签变量地址结构</param>
        /// <param name="size">长度</param>
        /// <returns></returns>
        public ItemData<string> ReadString(DeviceAddress address, ushort size)
        {
            var result = PlcReadWrite.ReadString(address.Addr, size);
            if (!result.IsSuccess)
                return new ItemData<string>(string.Empty, 0, QUALITIES.NG);
            else
                return new ItemData<string>(result.Content, 0, QUALITIES.OK);//是否考虑字节序问题？
        }

        /// <summary>
        /// 读取32位浮点数
        /// </summary>
        /// <param name="address">标签变量地址结构</param>
        /// <returns></returns>
        public ItemData<float> ReadFloat(DeviceAddress address)
        {
            var result = PlcReadWrite.ReadFloat(address.Addr);
            if (!result.IsSuccess)
                return new ItemData<float>(0.0f, 0, QUALITIES.NG);
            else return new ItemData<float>(result.Content, 0, QUALITIES.OK);//是否考虑字节序问题？
        }

        /// <summary>
        /// 读取1位
        /// </summary>
        /// <param name="address">标签变量地址结构体</param>
        /// <returns></returns>
        public ItemData<bool> ReadBit(DeviceAddress address)
        {
            var result = PlcReadWrite.ReadBool(address.Addr);
            if (!result.IsSuccess)
                return new ItemData<bool>(false, 0, QUALITIES.NG);
            else return new ItemData<bool>(result.Content, 0, QUALITIES.OK);//是否考虑字节序问题？
        }

        /// <summary>
        /// 写字节数组到设备
        /// </summary>
        /// <param name="address">标签变量地址结构</param>
        /// <param name="bits">需写的字节数组</param>
        /// <returns></returns>
        public int WriteBytes(DeviceAddress address, byte[] bits)
        {
            var result = PlcReadWrite.Write(address.Addr, bits);
            if (result.IsSuccess) return 0;
            else
            {
                OnError1(result.Message);
                return -result.ErrorCode;
            }
        }

        public int WriteBit(DeviceAddress address, bool bit)
        {
            var result = PlcReadWrite.Write(address.Addr, bit);
            if (result.IsSuccess) return 0;
            else
            {
                OnError1(result.Message);
                return -result.ErrorCode;
            }
        }

        public int WriteBits(DeviceAddress address, byte bits)
        {
            var result = PlcReadWrite.Write(address.Addr, bits);
            if (result.IsSuccess) return 0;
            else
            {
                OnError1(result.Message);
                return -result.ErrorCode;
            }
        }

        public int WriteInt16(DeviceAddress address, short value)
        {
            var result = PlcReadWrite.Write(address.Addr, value);
            if (result.IsSuccess) return 0;
            else
            {
                OnError1(result.Message);
                return -result.ErrorCode;
            }
        }

        public int WriteUInt16(DeviceAddress address, ushort value)
        {
            var result = PlcReadWrite.Write(address.Addr, value);
            if (result.IsSuccess) return 0;
            else
            {
                OnError1(result.Message);
                return -result.ErrorCode;
            }
        }

        public int WriteUInt32(DeviceAddress address, uint value)
        {
            var result = PlcReadWrite.Write(address.Addr, value);
            if (result.IsSuccess) return 0;
            else
            {
                OnError1(result.Message);
                return -result.ErrorCode;
            }
        }

        public int WriteInt32(DeviceAddress address, int value)
        {
            var result = PlcReadWrite.Write(address.Addr, value);
            if (result.IsSuccess) return 0;
            else
            {
                OnError1(result.Message);
                return -result.ErrorCode;
            }
        }

        public int WriteFloat(DeviceAddress address, float value)
        {
            var result = PlcReadWrite.Write(address.Addr, value);
            if (result.IsSuccess) return 0;
            else
            {
                OnError1(result.Message);
                return -result.ErrorCode;
            }
        }

        public int WriteString(DeviceAddress address, string str)
        {
            var result = PlcReadWrite.Write(address.Addr, str);
            if (result.IsSuccess) return 0;
            else
            {
                OnError1(result.Message);
                return -result.ErrorCode;
            }
        }

        public void CreateDefaultAddresses()
        {
        }

        public override bool CheckAddressLegal(string address, DataType dataType)
        {
            return true;
        }

        public override Dictionary<string, string> GetRegester()
        {
            return new Dictionary<string, string>()
            {
                { "0","0"},{ "1","1"},{ "2","2"},{ "3","3"},{ "4","4"}
            };
        }

        public virtual DeviceAddress GetDeviceAddress(TagConfig meta)
        {
            var address = meta.Address;
            DeviceAddress dv = DeviceAddress.Empty;
            dv.Addr = address;
            dv.VarType = meta.DataType;
            try
            {
                if (string.IsNullOrEmpty(address))
                    return dv;
                var sindex = address.IndexOf(':');
                if (sindex > 0)
                {
                    int slaveId;
                    if (int.TryParse(address.Substring(0, sindex), out slaveId))
                        dv.Area = slaveId;
                    address = address.Substring(sindex + 1);
                }
                switch (address[0])
                {
                    case '0':
                        {
                            dv.DBNumber = Modbus.fctReadCoil;
                            int st;
                            int.TryParse(address, out st);
                            //dv.Start = (st / 16) * 16;//???????????????????
                            dv.Bit = (byte)(st % 16);
                            st /= 16;
                            dv.Start = st;
                            //dv.Bit--;
                            dv.GatherType = GatherType.Digital;
                        }
                        break;

                    case '1':
                        {
                            dv.DBNumber = Modbus.fctReadDiscreteInputs;
                            int st;
                            int.TryParse(address.Substring(1), out st);
                            //dv.Start = (st / 16) * 16;//???????????????????
                            dv.Bit = (byte)(st % 16);
                            st /= 16;
                            dv.Start = st;
                            //dv.Bit--;
                            dv.GatherType = GatherType.Digital;
                        }
                        break;

                    case '4':
                        {
                            int index = address.IndexOf('.');
                            dv.DBNumber = Modbus.fctReadHoldingRegister;
                            if (index > 0)
                            {
                                dv.Start = int.Parse(address.Substring(1, index - 1));
                                dv.Bit = byte.Parse(address.Substring(index + 1));
                                dv.GatherType = GatherType.Digital;
                            }
                            else
                            {
                                dv.Start = int.Parse(address.Substring(1));
                                dv.GatherType = GatherType.Analogue;
                            }
                            //dv.Start--;
                            //dv.Bit--;
                            dv.ByteOrder = ByteOrder.Network;
                        }
                        break;

                    case '3':
                        {
                            int index = address.IndexOf('.');
                            dv.DBNumber = Modbus.fctReadInputRegister;
                            if (index > 0)
                            {
                                dv.Start = int.Parse(address.Substring(1, index - 1));
                                dv.Bit = byte.Parse(address.Substring(index + 1));
                            }
                            else
                            {
                                dv.Start = int.Parse(address.Substring(1));
                                dv.GatherType = GatherType.Analogue;
                            }
                            //dv.Start--;
                            //dv.Bit--;
                            dv.ByteOrder = ByteOrder.Network;
                        }

                        break;
                }
            }
            catch (Exception)
            {
                Logger.Warn($"{address}地址格式有误！");
            }
            return dv;
        }

        public string GetAddress(DeviceAddress address)
        {
            return string.Empty;
        }

        protected void OnError1(string message)
        {
            Logger.Error(message);
        }

        public int WriteDynamic(IIOTag tag, object value)
        {
            throw new NotImplementedException();
        }
    }
}